﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Dynamic;
using System.Reflection;
using System.Reflection.Emit;
using Vexe.Runtime.Extensions;

namespace CodeFound
{
    /// <summary>
    /// 转换属性对象
    /// </summary>
    public class CastProperty
    {
        public PropertyAccessorHandler FromProperty
        {
            get;
            set;
        }

        public PropertyAccessorHandler ToProperty
        {
            get;
            set;
        }
    }
    /// <summary>
    /// 属性访问器
    /// </summary>
    public class PropertyAccessorHandler
    {
        public PropertyAccessorHandler(PropertyInfo propInfo)
        {
            this.PropertyName = propInfo.Name;
            //var obj = Activator.CreateInstance(classType);
            //var getterType = typeof(FastPropertyAccessor.GetPropertyValue<>).MakeGenericType(propInfo.PropertyType);
            //var setterType = typeof(FastPropertyAccessor.SetPropertyValue<>).MakeGenericType(propInfo.PropertyType);

            //this.Getter = Delegate.CreateDelegate(getterType, null, propInfo.GetGetMethod());
            //this.Setter = Delegate.CreateDelegate(setterType, null, propInfo.GetSetMethod());

            if (propInfo.CanRead)
                this.Getter = propInfo.GetValue;

            if (propInfo.CanWrite)
                this.Setter = propInfo.SetValue;
        }
        public string PropertyName { get; set; }
        public Func<object, object[], object> Getter { get; private set; }
        public Action<object, object, object[]> Setter { get; private set; }
    }
    public class NoName
    {
        //public delegate To CopyDelegate<From,To>(From f, To t);
        //public static Dictionary<int, Delegate>  dictionary = new Dictionary<int, Delegate>();
        public static Dictionary<int, List<CastProperty>> mPropertiesListDic = new Dictionary<int, List<CastProperty>>();
        public static void Render<From>(From f)
        {
            Type fromType = typeof(From);
            int fromTypeHashCode = fromType.GetHashCode();
            Type toType = typeof(Model);
            Model model = new CodeFound.NoName.Model();
            List<CastProperty> mProperties = new List<CodeFound.CastProperty>();
            if (!mPropertiesListDic.ContainsKey(fromTypeHashCode))
            {
                PropertyInfo[] fromTypePropertyInfo = fromType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                PropertyInfo[] toTypePropertyInfo = toType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (PropertyInfo fp in fromTypePropertyInfo)
                {
                    foreach (PropertyInfo tp in toTypePropertyInfo)
                    {
                        if (fp.Name == tp.Name && fp.PropertyType == tp.PropertyType)
                        {
                            CastProperty cp = new CodeFound.CastProperty();
                            cp.FromProperty = new PropertyAccessorHandler(fp);
                            cp.ToProperty = new PropertyAccessorHandler(tp);
                            mProperties.Add(cp);
                        }
                    }
                }
                mPropertiesListDic.Add(fromTypeHashCode,mProperties);
            }
            mProperties = mPropertiesListDic[fromTypeHashCode];
            for (int i = 0; i < mProperties.Count; i++)
            {
                CastProperty cpn = mProperties[i];
                if (cpn.FromProperty.Getter != null && cpn.ToProperty.Setter != null)
                {
                    object value = cpn.FromProperty.Getter(f, null);
                    cpn.ToProperty.Setter(model, value, null);
                }
            }
            Render(model);
        }
        
        public static Model CopyMethod(ModelOther model1,Model model2)
        {
            model2.Name = model1.Name;
            model2.Pwd = model1.Pwd;
            return model2;
        }
        public static ModelFeild CopyMethod(ModelFeild model1,ModelFeild model2)
        {
            model2.Name = model1.Name;
            model2.Pwd = model1.Pwd;
            return model2;
        }
        public static Dictionary<long, Delegate> CopyDic = new Dictionary<long, Delegate>();
        public delegate To CopyDelegate<From,To>(From f,To t);
        public static To CopyEmit<From, To>(From f, To t)
        {
            Type fromType = typeof(From);
            Type toType = typeof(To);
            long key = (long)fromType.GetHashCode() << 32 | (long)toType.GetHashCode();
            CopyDelegate<From, To> copyDelegate;
            if (CopyDic.ContainsKey(key))
            {
                Delegate delegateTemp = CopyDic[key];
                copyDelegate = (CopyDelegate<From, To>)delegateTemp;
            }
            else
            {
                DynamicMethod CopyMethod = new DynamicMethod(key.ToString(), typeof(To), new Type[] { typeof(From), typeof(To) }, true);
                ILGenerator methodIL = CopyMethod.GetILGenerator();
                PropertyInfo[] fromTypePropertyInfo = fromType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                PropertyInfo[] toTypePropertyInfo = toType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
                foreach (PropertyInfo fp in fromTypePropertyInfo)
                {
                    foreach (PropertyInfo tp in toTypePropertyInfo)
                    {
                        if (fp.Name == tp.Name && fp.PropertyType == tp.PropertyType)
                        {
                            methodIL.Emit(OpCodes.Ldarg_1);
                            methodIL.Emit(OpCodes.Ldarg_0);
                            methodIL.Emit(OpCodes.Callvirt, fp.GetGetMethod());
                            methodIL.Emit(OpCodes.Callvirt, tp.GetSetMethod());
                        }
                    }
                }
                FieldInfo[] fromTypeFieldInfo = fromType.GetFields(BindingFlags.Public | BindingFlags.Instance);
                FieldInfo[] toTypeFieldInfo = toType.GetFields(BindingFlags.Public | BindingFlags.Instance);
                foreach (FieldInfo fi in fromTypeFieldInfo)
                {
                    foreach (FieldInfo ti in toTypeFieldInfo)
                    {
                        methodIL.Emit(OpCodes.Ldarg_1);
                        methodIL.Emit(OpCodes.Ldarg_0);
                        methodIL.Emit(OpCodes.Ldfld, fi);
                        methodIL.Emit(OpCodes.Stfld, ti);
                    }
                }
                methodIL.Emit(OpCodes.Ldarg_1);
                methodIL.Emit(OpCodes.Ret);
                Delegate delegateTemp = CopyMethod.CreateDelegate(typeof(CopyDelegate<From, To>));
                CopyDic.Add(key,delegateTemp);
                copyDelegate = (CopyDelegate<From, To>)delegateTemp;
            }
            return copyDelegate(f, t);
        }
        public static void RenderEmitPublic<From>(From f)
        {
            Model model = new Model();
            Type fromType = typeof(From);
            Type toType = typeof(Model);
            DynamicMethod CopyMethod = new DynamicMethod("methodCopy", typeof(Model), new Type[] { typeof(From), typeof(Model) },true);
            ILGenerator methodIL = CopyMethod.GetILGenerator();
            PropertyInfo[] fromTypePropertyInfo = fromType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            PropertyInfo[] toTypePropertyInfo = toType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (PropertyInfo fp in fromTypePropertyInfo)
            {
                foreach (PropertyInfo tp in toTypePropertyInfo)
                {
                    if (fp.Name == tp.Name && fp.PropertyType == tp.PropertyType)
                    {
                        methodIL.Emit(OpCodes.Ldarg_1);
                        methodIL.Emit(OpCodes.Ldarg_0);
                        methodIL.Emit(OpCodes.Callvirt, fp.GetGetMethod());
                        methodIL.Emit(OpCodes.Callvirt, tp.GetSetMethod());

                    }
                }
            }
            FieldInfo[] fromTypeFieldInfo = fromType.GetFields(BindingFlags.Public|BindingFlags.Instance);
            FieldInfo[] toTypeFieldInfo = toType.GetFields(BindingFlags.Public|BindingFlags.Instance);
            foreach (FieldInfo fi in fromTypeFieldInfo)
            {
                foreach (FieldInfo ti in toTypeFieldInfo)
                {
                    methodIL.Emit(OpCodes.Ldarg_1);
                    methodIL.Emit(OpCodes.Ldarg_0);
                    methodIL.Emit(OpCodes.Ldfld,fi);
                    methodIL.Emit(OpCodes.Stfld,ti);
                }
            }
            methodIL.Emit(OpCodes.Ldarg_1);
            methodIL.Emit(OpCodes.Ret);
            CopyDelegate<From, Model> copyDelegate = (CopyDelegate<From, Model>)CopyMethod.CreateDelegate(typeof(CopyDelegate<From, Model>));
            model= copyDelegate(f,model);
        }
        public static void RenderEmitInternal<From>(From f)
        {
            Model model = new Model();
            Type fromType = typeof(From);
            Type toType = typeof(Model);
            PropertyInfo[] fromTypePropertyInfo = fromType.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            PropertyInfo[] toTypePropertyInfo = toType.GetProperties(BindingFlags.Public | BindingFlags.Instance);

        }
        public static void Cast()
        {

        }
        public class Model
        {
            public string Name { get; set; }
            public int Pwd { get; set; }
        }
        public class ModelOther
        {
            public string Name { get; set; }
            public int Pwd { get; set; }
            public float Pc { get; set; }
        }
        public class ModelFeild
        {
            public string Name;
            public int Pwd;
        }
        public static void Render(Model model)
        {
            Console.WriteLine(model.Name);
            Console.WriteLine(model.Pwd);
        }
        public static void Switch(int n)
        {
            switch (n)
            {
                case 1:Console.WriteLine(1);break;
                case 2:Console.WriteLine(2);break;
                case 3:Console.WriteLine(3);break;
                default:Console.WriteLine(4);break;
            }
        }
        //public static void Main(string[] args)
        //{
        //    RenderEmitPublic(new //ModelOther
        //    { Name = "1", Pwd = 2});
        //    var u1 = new { Name = "1", Pwd = 2 };
        //    var u2 = new { Name = "1", Pwd = 2 };
        //    var u3 = new Model { Name="1",Pwd=2};
        //    string cc = "";
        //    cc += "2";
        //    string dd = "2";
        //    Console.WriteLine(dd.GetHashCode()+":"+dd.GetTypeCode());
        //    Console.WriteLine(cc.GetHashCode()+":"+cc.GetTypeCode());
        //    Console.WriteLine(cc.Equals(dd));
        //    Console.WriteLine(cc.Equals("2"));
        //    Console.WriteLine(u1.Equals(u2));
        //    Console.WriteLine(u1.Equals(u3));
        //    Console.WriteLine(u1.GetHashCode()+":"+u2.GetHashCode());
        //    Console.WriteLine(long.MaxValue.GetHashCode());
        //    //Run();
        //    var temp = new { Name = "1", Pwd = 2, Pc = 2 };
        //    var temp2=new { Name = "2", Pwd = 3, Pc = 2 };
        //    Type type = temp.GetType();
        //    var prop = type.GetProperty("Name");
        //    var de = prop.DelegateForGet();
        //    string name= (string)de.SafeInvoke(temp);
        //    Model model = new CodeFound.NoName.Model();
        //    model.Name = "m";
        //    model.Pwd = 123;
        //    var a= typeof(Model).GetProperty("Name").DelegateForGet<Model,string>();
        //    var b= a.SafeInvoke(model);
        //    model= CopyEmit(temp, model);
        //    model = CopyEmit(temp2,model);
        //    Render(new { Name = "1", Pwd = 2,Pc=2 });
        //    //Render(new { Name = "2", Pwd = 3 });
        //    //var array = new[] { new { Name = "1", Pwd = 2 } , new { Name = "2", Pwd = 3 } };
            
        //    //Render(array[0]);
        //    //Render(array[1]);

        //    //Model person=new CodeFound.NoName.Model();
        //    //person.Name = "333";
        //    //person.Pwd = 333;
        //    //Render(person);
        //    Console.ReadKey();
        //}
        public static void Run()
        {
            Render(new { Name = "1", Pwd = 2 });
            Render(new { Name = "2", Pwd = 3 });
        }
    }
}
